在範例Dessert App中利用timer來觀察更複雜的生命週期
專案中的class DessertTimer有一個startTime()與stopTimer()
class DessertTimer {
// The number of seconds counted since the timer started
var secondsCount = 0
private var handler = Handler()
private lateinit var runnable: Runnable
fun startTimer() {...}
fun stopTimer() {...}
}
藉由Runnable與Handler以後台程序執行startTime()時
執行時會log每秒印出 Timer is at : $secondsCount
在MainActivity宣告一個變數
private lateinit var dessertTimer : DessertTimer
並在onCreate()中實例化
dessertTimer = DessertTimer()
Activity生命週期
可以看出在activity顯現前,就會呼叫onStart(),而activity不可見之後,會呼叫onStop()
在這二個地方加入startTimer()與stopTimer()
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart Called")
}
override fun onStop(){
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}
按分享鍵時,會使activity進入onPause(),但並未onStop()
所以Timer()仍會繼續
按Home鍵到桌面,此時app進入onStop(),再使用最近任務將app叫回
又會進入onStart(),Timer也繼續執行
若將onStop()中的stopTimer()註解掉,且未完整結束/退出app
則app即使未顯示於螢幕,Timer()也會持續在後台執行與使用系統資源
這是一種memory leak
若dessertTimer.startTimer()置於onCreate()執行
則app從後台回到前台時,Timer()不會執行,因爲onCreate()在 app被onDestroy()只會執行一次
本段的重點爲:若在生命週期中設置了某些可能會一直使用資源的程式
同時也要注意什麼時候該設置結束的程式
當一個app中有許多類似Timer()這樣的程式須要注意何時結束時
可以使用lifecycle library
如上一段的例子,通常是activity在各個生命週期發生時,設置程式該做什麼,如startTimer(),stopTimer()
但使用lifecycle library則反過來,讓Timer()自我監控當生命週期改變時,該做什麼
lifecycle library主要有三個部分
LifecycleOwner
interfaceLifecycleObserver
interface在此範例app中,DessertTimer就是Lifecycle observers
因此,讓DessertTimer實作LifecycleObserver
interface
得以觀察activity的生命週期而自動進行對應動作
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
...
}
改寫的class DessertTimer包含二件事情
LifecycleObserver
interface再用lifecycle呼叫addObserver()將DessertTimer這個class加到觀察者,並初始化
實例化DessertTimer的時候,Lifecycle owner(MainActivity)將傳入本身的lifecycle
所以這個動作就連結了MainActivity與DessertTimer
init {
lifecycle.addObserver(this)
}
接著在對應生命週期發生時,所要執行的方法前加上annotation@OnLifecycleEvent
並將對應的生命週期事件作爲參數傳入
如圖,屬於activity的生命週期皆可以被觀察
因此我們要在activity的生命週期
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {...}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer() {...}
MainActivity就是本app的Lifecycle owner
而MainActivity繼承自上層的FragmentActivity已實作了LifecycleOwner
再來是將MainActivity自身的lifecycle狀態(Lifecycle class)
傳給DessertTimer的建構子
dessertTimer = DessertTimer(this.lifecycle) //this = 當前的MainActivity
至此就完成了DessertTimer對MainActivity的觀察
當MainActivity的生命週期改變時
DessertTimer即會依照設置的annotation,執行對應的方法
所以原先MainActivity onStart(),onStop()中執行的startTimer()與stopTimer()都不需要了
執行app,檢查timer print log的動作應皆相同
app不在當前畫面,剛進入後台時,還不會呼叫onDestroy()
等待使用者隨時可快速的返回app
但android管理所有資源,可能因爲app久未被使用或資源不夠
而將已進入後台的app完全關閉
以下用adb模擬android關閉app的情況(android api版本須爲28以上)
開啓Terminal,輸入adb,正常要看到Android Debug Bridge version X.XX.X
出現如圖這樣表示沒找到adb執行程式
找自己的Android SDK Location爲 C:\Users\user\AppData\Local\Android\Sdk\
但實際上adb.exe在platform-tools資料夾內
所以路徑是C:\Users\user\AppData\Local\Android\Sdk\platform-tools
再依照第二步,將SDK路徑加入系統變數中
進階系統設定->環境變數->系統變數
執行adb,看到版本號了
執行app並累積一些金額
按Home,將app收到後台
開啓Terminal,輸入adb shell am kill com.example.android.dessertclicker
此指令功能爲將後台的app強制結束
再將app調回前台顯示,原本從後台回到前台
預期app的內容應該是與進後台前相同
但被強制結束的app,可以回到前台之後,內容都被清空了
app的生命週期也從onCreate()開始執行,timer從0開始
android系統會自動保存已設置ID之view的狀態
儘量於app恢復執行時,顯示之前的狀態
但有些程式中的變數,android系統不認得
因此若須要保存的話,可用onSaveInstanceState()
包裝起來